// spritewindow.cpp : contains the class definition for the window that allows for
// sprite modification and setup
//$Id: SpriteWindow.cpp 40 2009-09-01 03:27:14Z brwarner2 $
#include "StdAfx.h"
#include "SpriteWindow.h"

//Class name for the preview window
static const char PREVIEW_CLASS[] = "PREVIEW_WND";
static const char IMAGEBOX_CLASS[] = "IMAGEBOX_WND";

CSpriteWindow::CSpriteWindow(void) : m_pSprite(0)
{
}

CSpriteWindow::~CSpriteWindow(void)
{
	if(m_pSprite != 0) delete m_pSprite;
	m_pSprite = 0;
}

void CSpriteWindow::WindowInfo(::WindowInfo &info)
{
	info.classname = "CSpriteWindow";
	info.windowName = "Sprite Editor";
	info.width = 250;
	info.height = 300;
}

void CSpriteWindow::CreateControls( void )
{
	//Get instance
	HINSTANCE hInstance = GetModuleHandle(0);

	//Create the frame rate control
	m_hFPS[1] = CreateWindowEx(0, "EDIT", 0, WS_CHILD | WS_VISIBLE | ES_MULTILINE, 0, 0, 30, 20, m_hWindow, (HMENU)IDE_FPS, hInstance, 0 );
	m_hFPS[0] = CreateWindowEx( 0, UPDOWN_CLASS, 0, WS_CHILD | WS_VISIBLE | UDS_SETBUDDYINT, 30, 0, 20, 20, m_hWindow, (HMENU)IDU_FPS, hInstance, 0);
	SendMessage( m_hFPS[0], UDM_SETBUDDY, (WPARAM)m_hFPS[1], 0 );
	SendMessage( m_hFPS[0], UDM_SETRANGE, 1, MAKELONG(1, UD_MAXVAL) );
	m_hFPS[2] = CreateWindowEx( 0, "STATIC", "Framerate:", WS_CHILD | WS_VISIBLE, 0, 0, 80, 20, m_hWindow, 0, hInstance, 0 );

	//Create the width control
	m_hWidth[1] = CreateWindowEx( 0, "EDIT", 0, WS_CHILD | WS_VISIBLE | ES_MULTILINE, 0, 0, 30, 20, m_hWindow, (HMENU)IDE_FPS, hInstance, 0 );
	m_hWidth[0] = CreateWindowEx( 0, UPDOWN_CLASS, 0, WS_CHILD | WS_VISIBLE | UDS_SETBUDDYINT, 30, 0, 20, 20, m_hWindow, (HMENU)IDU_WIDTH, hInstance, 0);
	SendMessage( m_hWidth[0], UDM_SETBUDDY, (WPARAM)m_hWidth[1], 0 );
	SendMessage( m_hWidth[0], UDM_SETRANGE, 1, MAKELONG(1, UD_MAXVAL) );
	m_hWidth[2] = CreateWindowEx( 0, "STATIC", "Width:", WS_CHILD | WS_VISIBLE, 0, 0, 80, 20, m_hWindow, 0, hInstance, 0 );

	//Create the height control
	m_hHeight[1] = CreateWindowEx( 0, "EDIT", 0, WS_CHILD | WS_VISIBLE | ES_MULTILINE, 0, 0, 30, 20, m_hWindow, (HMENU)IDE_HEIGHT, hInstance, 0 );
	m_hHeight[0] = CreateWindowEx( 0, UPDOWN_CLASS, 0, WS_CHILD | WS_VISIBLE | UDS_SETBUDDYINT, 30, 0, 20, 20, m_hWindow, (HMENU)IDU_HEIGHT, hInstance, 0);
	SendMessage( m_hHeight[0], UDM_SETBUDDY, (WPARAM)m_hHeight[1], 0 );
	SendMessage( m_hHeight[0], UDM_SETRANGE, 1, MAKELONG(1, UD_MAXVAL) );
	m_hHeight[2] = CreateWindowEx( 0, "STATIC", "Height:", WS_CHILD | WS_VISIBLE, 0, 0, 80, 20, m_hWindow, 0, hInstance, 0 );

	//Create the frames control
	m_hFrames[1] = CreateWindowEx( 0, "EDIT", 0, WS_CHILD | WS_VISIBLE | ES_MULTILINE, 0, 0, 30, 20, m_hWindow, (HMENU)IDE_FRAMES, hInstance, 0 );
	m_hFrames[0] = CreateWindowEx( 0, UPDOWN_CLASS, 0, WS_CHILD | WS_VISIBLE | UDS_SETBUDDYINT, 30, 0, 20, 20, m_hWindow, (HMENU)IDU_FRAMES, hInstance, 0);
	SendMessage( m_hFrames[0], UDM_SETBUDDY, (WPARAM)m_hFrames[1], 0 );
	SendMessage( m_hFrames[0], UDM_SETRANGE, 1, MAKELONG(1, UD_MAXVAL) );
	m_hFrames[2] = CreateWindowEx( 0, "STATIC", "Frames:", WS_CHILD | WS_VISIBLE, 0, 0, 80, 20, m_hWindow, 0, hInstance, 0 );

	//Create the preview window
	m_hPreview = CreateWindowEx( 0, PREVIEW_CLASS, 0, WS_CHILD | WS_VISIBLE, 20, 20, 23, 40, m_hWindow, 0, hInstance, 0 );

	//Create play and pause buttons
	m_hPlay[0] = CreateWindowEx( WS_EX_CLIENTEDGE, "BUTTON", ">", WS_CHILD | WS_VISIBLE, 50, 0, 15, 15, m_hWindow, (HMENU)IDB_PLAY, hInstance, 0 );
	m_hPlay[1] = CreateWindowEx( WS_EX_CLIENTEDGE, "BUTTON", "||", WS_CHILD | WS_VISIBLE, 80, 0, 15, 15, m_hWindow, (HMENU)IDB_PAUSE, hInstance, 0 );

	//Create the image box
	m_hIB = CreateWindowEx( 0, IMAGEBOX_CLASS, 0, WS_CHILD | WS_VISIBLE, 20, 20, 1, 1, m_hWindow, (HMENU)IDIB_IMAGE, hInstance, 0 );

	//Create save button
	m_hSave = CreateWindowEx( WS_EX_CLIENTEDGE, "BUTTON", "Save Info File", WS_CHILD | WS_VISIBLE, 0, 0, 75, 50, m_hWindow, (HMENU)IDB_SAVE, hInstance, 0 );
}

void CSpriteWindow::Paint( HDC hdc )
{
}

void CSpriteWindow::RegisterClasses(void)
{
	WNDCLASSEX wc;
	SecureZeroMemory(&wc, sizeof(wc));
	wc.cbSize = sizeof(wc);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
	wc.hCursor = LoadCursor(0, IDC_ARROW);
	wc.hInstance = GetModuleHandle(0);
	wc.lpfnWndProc = PreviewWindowCallback;
	wc.lpszClassName = PREVIEW_CLASS;
	RegisterClassEx(&wc);

	wc.lpfnWndProc = ImageBoxCallback;
	wc.lpszClassName = IMAGEBOX_CLASS;
	RegisterClassEx(&wc);
}

void CSpriteWindow::LoadImageFile(const std::string &filename)
{
	//Load the image file
	m_pSprite = LoadImageFromFile(filename);

	//Load the image information
	LoadInfoFile( filename + ".info" );
	m_height = m_pSprite->GetHeight();
	m_spriteFile = filename;

	//Change sprite on our child
	SendMessage( m_hWindow, WM_UPDATECHILD, 0, 0 );

	//Change control positions
	PositionControls();
}

void CSpriteWindow::SaveInfoFile(const std::string &filename)
{
	//Opens the info file
	std::ofstream info(filename.c_str());
	if(info.fail()) return;

	using std::endl;

	info << "frames:" << m_nFrames << endl;
	info << "frame-size:" << m_width << endl;
	info << "frame-rate:" << m_nFPS << endl;
	info << "height:" << m_height << endl;
	info.close();
}

void CSpriteWindow::LoadInfoFile( const std::string& filename )
{
	m_nFrames = m_nFPS = m_width = m_height = 0;
	do
	{
		std::ifstream info(filename.c_str());
		if(info.fail()) break;
		using namespace std;
		string line;
		while(getline(info, line))
		{
			stringstream str = stringstream(line);
			string name;
			getline(str,name, ':');
			unsigned int value;
			str >> value;

			if(name == "frames") m_nFrames = value;
			else if(name == "frame-size") m_width = value;
			else if(name == "frame-rate") m_nFPS = value;
			else if(name == "height") m_height = value;
		}
		info.close();
	}while(false);

	if( m_nFrames == 0 ) m_nFrames = 1;
	if( m_width == 0 ) m_width = m_pSprite->GetWidth();
	if( m_height == 0 ) m_height = m_pSprite->GetHeight();
	if( m_nFPS == 0 ) m_nFPS = 1;
}

void CSpriteWindow::PositionControls( void )
{
	//Get the sprite width and height
	unsigned int width = m_pSprite->GetWidth(), height = m_pSprite->GetHeight();

	//Resize the main window
	SetWindowPos( m_hWindow, 0, 0, 0, 20 + width + 80 + 50 + 20, 20 + height + 20 + m_height + 50, SWP_NOMOVE | SWP_NOZORDER );

	//Move the controls around
	HDWP hd = BeginDeferWindowPos(116);
		if(hd) hd = DeferWindowPos(hd, m_hFPS[1], 0, 20 + width + 80, 20, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hFPS[0], 0, 20 + width + 80 + 30, 20, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hFPS[2], 0, 20 + width, 20, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hWidth[1], 0, 20 + width + 80, 42, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hWidth[0], 0, 20 + width + 80 + 30, 42, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hWidth[2], 0, 20 + width, 42, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hHeight[1], 0, 20 + width + 80, 64, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hHeight[0], 0, 20 + width + 80 + 30, 64, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hHeight[2], 0, 20 + width, 64, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hFrames[1], 0, 20 + width + 80, 86, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hFrames[0], 0, 20 + width + 80 + 30, 86, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hFrames[2], 0, 20 + width, 86, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hPreview, 0, 20, 20 + height + 20, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hPlay[0], 0, 20, 20 + height + 20 + m_height, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hPlay[1], 0, 20 + 15, 20 + height + 20 + m_height, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
		if(hd) hd = DeferWindowPos(hd, m_hSave, 0, 20 + width + 20, 20 + height + 20, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
	if(hd) EndDeferWindowPos(hd);
	InvalidateRect(m_hWindow, 0, true);
}

LRESULT CSpriteWindow::Message(UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch( msg )
	{
	case WM_CREATE:
		{
			m_width = 0;
			m_height = 0;
			m_nFPS = 0;

			//Remove frame
			LONG style = GetWindowLong(m_hWindow, GWL_STYLE);
			style &= ~WS_THICKFRAME;
			SetWindowLong(m_hWindow, GWL_STYLE, style);

			//Create the controls for the window
			CreateControls();

			SendMessage(m_hFPS[0], UDM_SETPOS, 0, MAKELONG(m_nFPS,0)); 
			SendMessage(m_hWidth[0], UDM_SETPOS, 0, MAKELONG(m_width,0));
		}break;
	case WM_UPDATECHILD:
		{
			//Update sprite object
			SendMessage( m_hPreview, WM_SETSPRITE, 0, reinterpret_cast<LPARAM>(m_pSprite) );
			SendMessage( m_hIB, WM_SETSPRITE, 0, reinterpret_cast<LPARAM>(m_pSprite) );

			//Update all other properties
			SendMessage(m_hPreview, WM_SETFPS, m_nFPS, 0);
			SendMessage(m_hPreview, WM_SETFRAMES, m_nFrames, 0);
			SetWindowPos(m_hPreview, 0, 0, 0, m_width, m_height, SWP_NOZORDER | SWP_NOMOVE);

			//Update size of image box
			unsigned int w = m_pSprite->GetWidth(), h = m_pSprite->GetHeight();
			SetWindowPos(m_hIB, 0, 0, 0, w, h, SWP_NOMOVE | SWP_NOZORDER);
			RECT rc = { 0, 0, m_width, m_height };
			SendMessage(m_hIB, WM_SETIMGRECT, 0, reinterpret_cast<LPARAM>(&rc));

			//Update updown counter
			SendMessage(m_hFPS[0], UDM_SETPOS, 0, MAKELONG(m_nFPS,0)); 
			SendMessage(m_hWidth[0], UDM_SETPOS, 0, MAKELONG(m_width,0));
			SendMessage(m_hHeight[0], UDM_SETPOS, 0, MAKELONG(m_height,0));
			SendMessage(m_hFrames[0], UDM_SETPOS, 0, MAKELONG(m_nFrames,0));

			//Redraw it
			InvalidateRect(m_hPreview, 0, true);
			InvalidateRect(m_hIB, 0, true);
		}break;
	case WM_NOTIFY:
		{
			//Get structure
			LPNMHDR nmhdr = reinterpret_cast<LPNMHDR>(lParam);
			if( nmhdr->code == UDN_DELTAPOS ) //Position change
			{
				//Get information and update FPS count
				LPNMUPDOWN ud = reinterpret_cast<LPNMUPDOWN>(lParam);
				if(ud->iPos + ud->iDelta <= 0) break;
				switch( nmhdr->idFrom )
				{
				case IDU_FPS: //FPS Counter
					{
						m_nFPS = ud->iPos + ud->iDelta;
						SendMessage( m_hPreview, WM_SETFPS, m_nFPS, 0 );
					}break;
				case IDU_WIDTH: //Width counter
					{
						m_width = ud->iPos + ud->iDelta;
						PostMessage(m_hWindow, WM_UPDATECHILD, 0, 0);
					}break;
				case IDU_HEIGHT:
					{
						m_height = ud->iPos + ud->iDelta;
						PostMessage(m_hWindow, WM_UPDATECHILD, 0, 0);
					}break;
				case IDU_FRAMES:
					{
						m_nFrames = ud->iPos + ud->iDelta;
						PostMessage(m_hWindow, WM_UPDATECHILD, 0, 0);
					}break;
				}
			}
			switch( nmhdr->idFrom )
			{
			case IDIB_IMAGE: //Image box
				{
					if( nmhdr->code == IBN_CLICKRECT ) //User is dragging rect
					{
						//Get information and update all controls
						NMHDIBCLICK* hib = reinterpret_cast<NMHDIBCLICK*>(lParam);
						m_width = hib->newRect.right;
						m_height = hib->newRect.bottom;
						SendMessage(m_hWindow, WM_UPDATECHILD, 0, 0);
					}
				}break;
			}
		}break;
	case WM_COMMAND:
		{
			//If text box change command
			if( HIWORD(wParam) == EN_CHANGE )
			{
				//Get the text box and buddy
				HWND hUD[2];
				hUD[1] = GetDlgItem(m_hWindow, LOWORD(wParam));
				hUD[0] = GetDlgItem(m_hWindow, LOWORD(wParam)-1);

				//Cancel on empty box
				if( GetWindowTextLength( hUD[1] ) == 0 ) break;

				//Call GETPOS to update the up/down counter
				LRESULT ret = SendMessage( hUD[0], UDM_GETPOS, 0, 0  );
				if( HIWORD(ret) != 0 ) break;

				unsigned int newPos = LOWORD(ret);

				//Prepare a notify that the position has changed
				NMUPDOWN hdr;
				hdr.hdr.code = UDN_DELTAPOS;
				hdr.hdr.hwndFrom = hUD[0];
				hdr.hdr.idFrom = GetDlgCtrlID(hUD[0]);
				hdr.iPos = newPos;
				hdr.iDelta = 0;
				SendMessage( m_hWindow, WM_NOTIFY, (WPARAM)hdr.hdr.hwndFrom, (LPARAM)&hdr );
			}

			//Check control issuing command
			switch( LOWORD( wParam ) )
			{
			case IDB_PLAY:
				{
					SendMessage( m_hPreview, WM_PLAY, 1, 0 );
				}break;
			case IDB_PAUSE:
				{
					SendMessage( m_hPreview, WM_PLAY, 0, 0 );
				}break;
			case IDB_SAVE:
				{
					SaveInfoFile( m_spriteFile + ".info" );
				}break;
			}
		}break;
	default:
		return DefWindowProc( m_hWindow, msg, wParam, lParam );
	}
	return 0;
}

LRESULT CALLBACK CSpriteWindow::ImageBoxCallback(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
#define GetImageInfo() reinterpret_cast<ImageBoxInfo*>(GetWindowLongPtr(hWnd, GWLP_USERDATA))
	switch(msg)
	{
	case WM_CREATE:
		{
			//Craete an info structure
			ImageBoxInfo *pInfo = new ImageBoxInfo();
			SecureZeroMemory(pInfo, sizeof(ImageBoxInfo));
			SetWindowLongPtr( hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pInfo) );
		}break;
	case WM_SETSPRITE:
		{
			//Get the sprite object
			Gdiplus::Image* pSprite = reinterpret_cast<Gdiplus::Image*>(lParam);

			//Get our image information and store sprite
			GetImageInfo()->sprite = pSprite;
		}break;
	case WM_SETIMGRECT:
		{
			//Get the rectangle
			RECT *rc = reinterpret_cast<RECT*>(lParam);
			
			//Store it
			ImageBoxInfo *pInfo = GetImageInfo();
			pInfo->width = rc->right;
			pInfo->height = rc->bottom;
		}break;
	case WM_PAINT:
		{
			//Get information structure
			ImageBoxInfo *pInfo = GetImageInfo();
			if( pInfo->sprite == 0 ) break;

			//Begin the paint operation
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint(hWnd, &ps);

			//Start drawing operations
			using namespace Gdiplus;
			Graphics g(hdc);
			Rect rc(0, 0, pInfo->sprite->GetWidth(), pInfo->sprite->GetHeight());
			g.DrawImage( pInfo->sprite, rc, 0, 0, rc.Width, rc.Height, UnitPixel );

			//Draw the grid
			Pen p( Color(0, 0, 0) );
			p.SetDashStyle( DashStyleDash );
			unsigned int w = pInfo->sprite->GetWidth(), h = pInfo->sprite->GetHeight();
			for( unsigned int i = 0; i < w / pInfo->width; i++ )
			{
				g.DrawLine(&p, i*pInfo->width, 0, i*pInfo->width, h);
			}
			for( unsigned int i = 0; i < h / pInfo->height; i++ )
			{
				g.DrawLine(&p, 0, i*pInfo->height, w, i*pInfo->height);
			}

			//Draw a box to represent display area
			SolidBrush b( Color( 175, 0, 0, 255 ) );
			g.FillRectangle(&b, 1, 1, pInfo->width-1, pInfo->height-1);

			//End paint cycle
			EndPaint(hWnd, &ps);
		}break;
	case WM_LBUTTONDOWN:
		{
			//Prepare a structure containing the notification information
			NMHDIBCLICK hdr;
			hdr.hdr.code = IBN_CLICKRECT;
			hdr.hdr.hwndFrom = hWnd;
			hdr.hdr.idFrom = GetDlgCtrlID(hWnd);
			SecureZeroMemory(&hdr.newRect, sizeof(RECT));

			//Include new position
			hdr.newRect.right = LOWORD(lParam);
			hdr.newRect.bottom = HIWORD(lParam);

			//Send notify to parent
			SendMessage( GetParent(hWnd), WM_NOTIFY, (WPARAM)hWnd, (LPARAM)&hdr );

			//Start dragging
			GetImageInfo()->bDragged = true;

			//Begin mouse tracking to check for mouse leave event
			TRACKMOUSEEVENT tme;
			tme.cbSize = sizeof(TRACKMOUSEEVENT);
			tme.dwFlags = TME_LEAVE;
			tme.dwHoverTime = HOVER_DEFAULT;
			tme.hwndTrack = hWnd;
			TrackMouseEvent(&tme);
		}break;
	case WM_MOUSEMOVE:
		{
			//If we are in "drag" mode
			if( GetImageInfo()->bDragged )
			{
				//Prepare a structure containing the notification information
				NMHDIBCLICK hdr;
				hdr.hdr.code = IBN_CLICKRECT;
				hdr.hdr.hwndFrom = hWnd;
				hdr.hdr.idFrom = GetDlgCtrlID(hWnd);
				SecureZeroMemory(&hdr.newRect, sizeof(RECT));

				//Include new position
				hdr.newRect.right = LOWORD(lParam);
				hdr.newRect.bottom = HIWORD(lParam);

				//Send notification
				SendMessage( GetParent(hWnd), WM_NOTIFY, (WPARAM)hWnd, (LPARAM)&hdr );
			}
		}break;
	case WM_MOUSELEAVE:
		{
			//Stop dragging
			GetImageInfo()->bDragged = false;
		}break;
	case WM_LBUTTONUP:
		{
			//Cancel the mouse tracking
			TRACKMOUSEEVENT tme;
			tme.cbSize = sizeof(TRACKMOUSEEVENT);
			tme.dwFlags = TME_CANCEL | TME_LEAVE;
			tme.dwHoverTime = HOVER_DEFAULT;
			tme.hwndTrack = hWnd;
			TrackMouseEvent(&tme);
			GetImageInfo()->bDragged = false;
		}break;
	default:
		return DefWindowProc(hWnd, msg, wParam, lParam);
	}
	return 0;
#undef GetImageInfo
}

LRESULT CALLBACK CSpriteWindow::PreviewWindowCallback(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
#define GetImageInfo() reinterpret_cast<ImageInfo*>(GetWindowLongPtr(hWnd, GWLP_USERDATA))
	switch(msg)
	{
	case WM_CREATE:
		{
			//Create an info structure
			ImageInfo *pInfo = new ImageInfo();
			SecureZeroMemory(pInfo, sizeof(ImageInfo));
			SetWindowLongPtr( hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pInfo) );
		}break;
	case WM_SETSPRITE:
		{
			//Get the sprite object
			Gdiplus::Image* pSprite = reinterpret_cast<Gdiplus::Image*>(lParam);

			//Get our image information
			ImageInfo *pInfo = GetImageInfo();
			pInfo->sprite = pSprite; //Save sprite
		}break;
	case WM_SETFPS:
		{
			//Save FPS
			GetImageInfo()->fps = wParam;
		}break;
	case WM_SETFRAMES:
		{
			ImageInfo* pInfo = GetImageInfo();

			//Save frame count
			pInfo->frames = wParam;

			//Validate play cycle
			if( pInfo->currentFrame >= pInfo->frames ) pInfo->currentFrame = pInfo->frames-1;
			
		}break;
	case WM_PLAY:
		{
			//Make sure all our info is valid before we allow ourselves to play or pause
			ImageInfo* pInfo = GetImageInfo();
			if( pInfo->fps <= 0 || pInfo->frames <= 0 || pInfo->sprite == 0 ) break;

			//Check if we are pausing or playing
			bool bPlay = (wParam>0);
			if( bPlay == pInfo->playing ) break;
			if(bPlay)
			{
				//Create a new timer for the play cycle
				pInfo->timerID = SetTimer(hWnd, FRAME_TIMER, 1000 / pInfo->fps, 0);
			}
			else
			{
				//Kill the timer
				KillTimer(hWnd, FRAME_TIMER);
				pInfo->timerID = 0;
			}
			pInfo->playing = bPlay;
		}break;
	case WM_TIMER:
		{
			//Make sure it is the FPS timer
			ImageInfo* pInfo = GetImageInfo();
			if( wParam == FRAME_TIMER )
			{
				//Increment the frame counter
				pInfo->currentFrame++;
				if( pInfo->currentFrame >= pInfo->frames ) pInfo->currentFrame = 0;

				//Redraw
				InvalidateRect(hWnd, 0, true);

				//Recreate timer
				pInfo->timerID = SetTimer(hWnd, FRAME_TIMER, 1000 / pInfo->fps, 0);
			}
		}break;
	case WM_PAINT:
		{
			//Get our image information
			ImageInfo* pInfo = GetImageInfo();
			if( pInfo->sprite == 0 ) break;

			//Begin painting
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint(hWnd, &ps);

			//Create a graphics structure
			using namespace Gdiplus;
			Graphics g(hdc);

			//Get our client rect
			RECT rc;
			GetClientRect(hWnd, &rc);

			//Destination rectangle
			Rect dest(0, 0, rc.right, rc.bottom);

			//Draw the sprite
			g.DrawImage( pInfo->sprite, dest, rc.right*pInfo->currentFrame, 0, rc.right, rc.bottom, UnitPixel );

			//End painting
			EndPaint(hWnd, &ps);
		}break;
	case WM_DESTROY:
		{
			//Destroy the image information
			ImageInfo* pInfo = GetImageInfo();
			delete pInfo;
			SetWindowLongPtr( hWnd, GWLP_USERDATA, 0 );
		}break;
	default:
		{
			return DefWindowProc(hWnd, msg, wParam, lParam);
		}
	}
	return 0;
#undef GetImageInfo
}